use std::{
    env,
    fs::File,
    io::{self, BufRead, BufReader},
    process,
};

fn main() -> anyhow::Result<()> {
    let args: Vec<_> = env::args().collect();

    if args.len() < 2 {
        println!("Usage: cargo run -- <[-|<filename>]>");
        process::exit(1);
    }

    let arg = &args[1];

    {
        println!("Starting on-stack dynamic dispatch");

        // These must live longer than `readable`, and thus are declared first:
        let (mut stdin_read, mut file_read);

        // We need to describe the type to get dynamic dispatch.
        let readable: &mut dyn io::Read = if arg == "-" {
            stdin_read = io::stdin();

            &mut stdin_read
        } else {
            file_read = File::open(arg)?;

            &mut file_read
        };

        // Read from `readable` here.
        let mut buffered_reader = BufReader::new(readable);
        let mut line = String::new();
        while buffered_reader.read_line(&mut line)? > 0usize {
            println!("Read: {line}");
            line.clear();
        }

        println!("Done with on-stack dynamic dispatch");
    }

    {
        println!("Starting Box-based dynamic dispatch");

        // We still need to ascribe the type for dynamic dispatch.
        let readable: Box<dyn io::Read> = if arg == "-" {
            Box::new(io::stdin())
        } else {
            Box::new(File::open(arg)?)
        };

        // Read from `readable` here.
        let mut buffered_reader = BufReader::new(readable);
        let mut line = String::new();
        while buffered_reader.read_line(&mut line)? > 0usize {
            println!("Read: {line}");
            line.clear();
        }

        println!("Done with Box-based dynamic dispatch");
    }

    Ok(())
}
